{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 0.导入库"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入常用的库\n",
    "import time\n",
    "import string\n",
    "import random\n",
    "# 导入加密解密的库hashlib\n",
    "import hashlib\n",
    "# 导入发起网络请求的库requests\n",
    "import requests\n",
    "# 导入字典的易用库easydict\n",
    "from easydict import EasyDict\n",
    "# 导入解析符合xml规范字符串的库lxml\n",
    "from lxml import etree\n",
    "# 导入代码文件config.py中的字典config_dict\n",
    "from config import config_dict\n",
    "# 导入把url链接转换为二维码图像的库qrcode\n",
    "import qrcode"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. 定义方法和类"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "class WechatPayError(Exception):\n",
    "    def __init__(self, message):\n",
    "        super(WeixinPayError, self).__init__(message)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "class WechatPay(object):\n",
    "    # 把config_dict的键值对传递给对象的属性default_dict\n",
    "    def __init__(self):\n",
    "        self.default_dict = EasyDict()\n",
    "        self.appid = config_dict['appid']\n",
    "        self.mch_id = config_dict['mch_id']\n",
    "        self.password = config_dict['password']\n",
    "    \n",
    "    # 获取随机的字符串\n",
    "    def get_nonce_str(self):\n",
    "        char_list = string.ascii_letters + string.digits\n",
    "        nonce_str = \"\".join(random.choice(char_list) for _ in range(32))\n",
    "        return nonce_str\n",
    "    \n",
    "    # 获取签名\n",
    "    def get_sign(self, argument_dict):\n",
    "        sorted_keys = sorted(argument_dict.keys())\n",
    "        hash_string = ''\n",
    "        for i, key in enumerate(sorted_keys):\n",
    "            value = str(argument_dict[key])\n",
    "            if value != \"\" and i > 0:\n",
    "                hash_string += '&%s=%s' %(key, value)\n",
    "            elif value != \"\" and i == 0:\n",
    "                hash_string += '%s=%s' %(key, value)\n",
    "        hash_string += \"&key=%s\" %self.password\n",
    "        hash_bytes = hash_string.encode('utf-8')\n",
    "        hash_result = hashlib.md5(hash_bytes).hexdigest()\n",
    "        sign = hash_result.upper()\n",
    "        return sign\n",
    "    \n",
    "    # 把表示参数的字典转换为xml格式的字符串 \n",
    "    def to_xml(self, argument_dict):\n",
    "        xml_string = \"\"\n",
    "        for key, value in argument_dict.items():\n",
    "            xml_string += '<{0}>{1}</{0}>'.format(key, value)\n",
    "        xml_string = '<xml>%s</xml>' %xml_string\n",
    "        return xml_string\n",
    "    \n",
    "    # 把xml格式的字符串转换为字典\n",
    "    def to_dict(self, xml_string):\n",
    "        result_dict = {}\n",
    "        root = etree.fromstring(xml_string)\n",
    "        for child in root:\n",
    "            result_dict[child.tag] = child.text\n",
    "        return result_dict\n",
    "    \n",
    "    # 获取表示响应信息的字典\n",
    "    def get_response_dict(self, url, **argument_dict):\n",
    "        # 根据密钥生成签名\n",
    "        argument_dict['nonce_str'] = self.get_nonce_str()\n",
    "        argument_dict['sign'] = self.get_sign(argument_dict)\n",
    "        header_dict = {'Content-Type': 'text/xml; charset=UTF-8'}\n",
    "        xml_string = self.to_xml(argument_dict)\n",
    "        xml_bytes = xml_string.encode('utf8')\n",
    "        response = requests.post(url, data=xml_bytes, headers=header_dict)\n",
    "        response.encoding = 'utf8'\n",
    "        response_content = response.text\n",
    "        response_dict = self.to_dict(response_content)\n",
    "        return response_dict\n",
    "    \n",
    "    # 创建支付订单\n",
    "    def open_order(self, **kwargs):\n",
    "        url = 'https://api.mch.weixin.qq.com/pay/unifiedorder'\n",
    "        argument_dict = EasyDict()\n",
    "        argument_dict.appid = self.appid\n",
    "        argument_dict.mch_id = self.mch_id\n",
    "        argument_dict.device_info = 'web'\n",
    "        argument_dict.body = 'Native支付测试'\n",
    "        argument_dict.spbill_create_ip = '192.168.0.153'\n",
    "        argument_dict.notify_url = 'http://wxpay.wxutil.com/pub_v2/pay/notify.v2.php'\n",
    "        argument_dict.trade_type = 'NATIVE'\n",
    "        argument_dict.update(kwargs)\n",
    "        response_dict = self.get_response_dict(url, **argument_dict)\n",
    "        return response_dict\n",
    "    \n",
    "    def query_order(self, **kwargs):\n",
    "        url = 'https://api.mch.weixin.qq.com/pay/orderquery' \n",
    "        argument_dict = EasyDict()\n",
    "        argument_dict.appid = self.appid\n",
    "        argument_dict.mch_id = self.mch_id\n",
    "        argument_dict.update(kwargs)\n",
    "        response_dict = self.get_response_dict(url, **argument_dict)\n",
    "        return response_dict\n",
    "\n",
    "\n",
    "# 获取二维码图像\n",
    "def get_qrcode_image(url):\n",
    "    qrcode_maker = qrcode.QRCode()\n",
    "    qrcode_maker.add_data(url)\n",
    "    qrcode_image = qrcode_maker.make_image()\n",
    "    return qrcode_image    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1.创建订单示例"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [],
   "source": [
    "wechat_pay = WechatPay()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'return_code': 'SUCCESS',\n",
       " 'return_msg': 'OK',\n",
       " 'appid': 'wx820bee8852e87032',\n",
       " 'mch_id': '1521151201',\n",
       " 'device_info': 'web',\n",
       " 'nonce_str': 'XxRPUGKQrat655ag',\n",
       " 'sign': '23AC71F9CC8FF0D96B59F18D72EB61D9',\n",
       " 'result_code': 'SUCCESS',\n",
       " 'prepay_id': 'wx0418190094100316b0a16c9f1585771000',\n",
       " 'trade_type': 'NATIVE',\n",
       " 'code_url': 'weixin://wxpay/bizpayurl?pr=4TKBLjy'}"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "out_trade_no = '10000007'\n",
    "total_fee = 1\n",
    "response_dict = weixin_pay.open_order(out_trade_no=out_trade_no, total_fee=total_fee)\n",
    "response_dict"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAQUAAAD8CAYAAAB+fLH0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAABadJREFUeJzt3dFu4ygAQNF2tf//y92XHWnuKKlKBwKuz3mOYjeNrhDE8P7x8fEG8Ms/u28AOIsoACEKQIgCEKIAhCgAIQpAiAIQogDEv7tv4H9+VgnrvX/lRUYKQIgCEKIAhCgAIQpAiAIQogCEKAAhCkCIAhCiAIQoACEKQIgCEKIAhCgAIQpAiAIQogCEKAAhCkCIAhCiAMQp5z4s9f7+pe3ut/r4GDv6YsbfNOuao+8z6tF1d3xeq63+HL/KSAEIUQBCFIAQBSBuMdH4yK5JndEJr5HJvdGJwNF7OWUi7O1t3qTnVb4Hr2SkAIQoACEKQIgCEKIAxG1XH56ZNSs8a1Z7xvusXmWYtRKw62fUj5z2PXglIwUgRAEIUQBCFIAQBSCsPvwgo884zHr9jk1WnrnibP9pjBSAEAUgRAEIUQBCFICw+nC4lTsvrbZydyGrDOsYKQAhCkCIAhCiAIQoAGH14Q+nzWqPPJ+w+nyHk84qWL3Sctr34JWMFIAQBSBEAQhRAEIUgLjt6sNJM+mfOenU6Wd27Pg0696v8j14JSMFIEQBCFEAQhSAEAUg3g/5jfcRN/FTrX5OYPW5EiPPefCpLy21GCkAIQpAiAIQogCEKABxi2cfZuxStOv1z/zEGfmTnkNY+X/6zvu8kpECEKIAhCgAIQpAiAIQt1h9eGT1rkOjrz9plnr0Xq6889Ksv2n0/U9ePTJSAEIUgBAFIEQBCJus/GHXNumjrjABOWtSboZdP18f8YL/qU1WgHGiAIQoACEKQIgCELdYfVi5QrB6m/SR667eGGTkXma+/4xrPnPSz8tfwOoDME4UgBAFIEQBCFEA4rabrJx2uOqs6854j9F737FhzUnbwf80RgpAiAIQogCEKAAhCkDcYvVhxqz2Sdubj1r9+/5ZKy0z7nPHwb6znPK8hZECEKIAhCgAIQpAiAIQt1h9eOS0GfmVs+anncuw4wyNk1YZTmekAIQoACEKQIgCEKIAxG3PfThpdeA7RmbHT7qXz4x8xiedt3Ehzn0AxokCEKIAhCgAIQpA3PbZh2dWr0rMuu6Iq5+svHL3qZXXHL3uKf8PIwUgRAEIUQBCFIAQBSBu8ezDI1efkR+x+nmO1bsUPbrujpOuPzPjfl7w3fPsAzBOFIAQBSBEAQhRAOIWzz7M2HlptR2/73/mKqsSI1bvnHWFz+CrjBSAEAUgRAEIUQBCFIC4xerDyG/nV59OvHJmf9YM+GmrGH/72lnX/M77XHFVwkgBCFEAQhSAEAUgbjHROOOA0l0TkzPs+tnyrgnOk6558oTiM0YKQIgCEKIAhCgAIQpA3HaL96uYsepx0mG333n/lT9Tf2bH32qLd+BIogCEKAAhCkCIAhC3ffbhNKObdDyyayORWVaueqzeyn30uiczUgBCFIAQBSBEAQhRAOIWqw+P7HrmY+Vs9KzZ+9WfzYz72fU32eIduB1RAEIUgBAFIEQBiNuuPjyzesedUSP3s2sG/KSZ9NX3cshOZUsZKQAhCkCIAhCiAIQoAGH14XB32nlpxGnPZ5y0AvO3jBSAEAUgRAEIUQBCFICw+nC4Gc8+jK4mrD6l+qRVjFmrDD/pPAgjBSBEAQhRAEIUgBAFIKw+/OGkmfG3tz0nMc96/ej7rPxbT9uV6rTv2e+MFIAQBSBEAQhRAEIUgLjt6sPJvz3/3crZ66ucUj3iKrtMPbruKZ+jkQIQogCEKAAhCkC8HzK5ccRNwA/3pRlkIwUgRAEIUQBCFIAQBSBEAQhRAEIUgBAFIEQBCFEAQhSAEAUgRAEIUQBCFIAQBSBEAQhRAEIUgBAFIEQBCFEA4pQDZq9x2ivcgJECEKIAhCgAIQpAiAIQogCEKAAhCkCIAhCiAIQoACEKQIgCEKIAhCgAIQpAiAIQogCEKAAhCkCIAhCiAIQoAPEfLM5EJCXtvHoAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "url = response_dict['code_url']\n",
    "qrcode_image = get_qrcode_image(url)\n",
    "import matplotlib.pyplot as plt\n",
    "plt.imshow(qrcode_image)\n",
    "plt.axis('off')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2.未支付时，查询订单"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'return_code': 'SUCCESS',\n",
       " 'return_msg': 'OK',\n",
       " 'appid': 'wx820bee8852e87032',\n",
       " 'mch_id': '1521151201',\n",
       " 'device_info': 'web',\n",
       " 'nonce_str': '9q8wcYnAmiVRtuRL',\n",
       " 'sign': 'A1B9A836674D6244BC6BC354805BC0A0',\n",
       " 'result_code': 'SUCCESS',\n",
       " 'total_fee': '1',\n",
       " 'out_trade_no': '10000007',\n",
       " 'trade_state': 'NOTPAY',\n",
       " 'trade_state_desc': '订单未支付'}"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response_dict = wechat_pay.query_order(out_trade_no=out_trade_no)\n",
    "response_dict"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3.用手机支付后，查询订单"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'return_code': 'SUCCESS',\n",
       " 'return_msg': 'OK',\n",
       " 'appid': 'wx820bee8852e87032',\n",
       " 'mch_id': '1521151201',\n",
       " 'device_info': 'web',\n",
       " 'nonce_str': 'lnzcsJKmtUlCsUj7',\n",
       " 'sign': '5D2365A4A869EAF5F355D1E1A9D500EB',\n",
       " 'result_code': 'SUCCESS',\n",
       " 'openid': 'o1QoA0wQ2B3iiFJTlMmMXaLZwfco',\n",
       " 'is_subscribe': 'N',\n",
       " 'trade_type': 'NATIVE',\n",
       " 'bank_type': 'CFT',\n",
       " 'total_fee': '1',\n",
       " 'fee_type': 'CNY',\n",
       " 'transaction_id': '4200000346201908046844648327',\n",
       " 'out_trade_no': '10000007',\n",
       " 'attach': '',\n",
       " 'time_end': '20190804182203',\n",
       " 'trade_state': 'SUCCESS',\n",
       " 'cash_fee': '1',\n",
       " 'trade_state_desc': '支付成功'}"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response_dict = wechat_pay.query_order(out_trade_no=out_trade_no)\n",
    "response_dict"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
